package com.mspbots.core.ticketai.service.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.mspbots.core.AbstractBaseServiceImpl;
import com.mspbots.core.ticketai.mapper.TicketRuleMapper;
import com.mspbots.core.ticketai.model.Board;
import com.mspbots.core.ticketai.model.TicketRule;
import com.mspbots.core.ticketai.service.BoardService;
import com.mspbots.core.ticketai.service.TicketRuleService;
import com.mspbots.core.wise.model.Tenant;
import com.mspbots.core.wise.service.TenantService;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * com.mspbots.core.ticketai.service.impl.TicketRuleServiceImpl
 *
 * @author Alex bob(https://github.com/vnobo)
 * @date Created by 2019/8/20
 */
@Service
@RequiredArgsConstructor
@CacheConfig(cacheNames = "cacheing:mspbots:ticketrule")
public class TicketRuleServiceImpl extends AbstractBaseServiceImpl<TicketRuleMapper, TicketRule>
        implements TicketRuleService {

    private final TenantService tenantService;
    private final BoardService boardService;


    @Override
    public List<TicketRule> saveBatchAll(List<TicketRule> ticketRules) {
        List<TicketRule> result = new ArrayList<>(ticketRules);
        ticketRules.stream()
                .filter(t -> !CollectionUtils.isEmpty(t.getApplyToBoards()))
                .forEach(t -> {
                    List<TicketRule> otherBoards = super.list(Wrappers.<TicketRule>lambdaQuery()
                            .eq(TicketRule::getTenantId, t.getTenantId())
                            .in(TicketRule::getBoard, t.getApplyToBoards())
                            .eq(TicketRule::getName, t.getName())
                            .eq(TicketRule::getEnable, true));
                    t.getApplyToBoards().stream()
                            .filter(b -> !t.getBoard().equals(b)).forEach(b -> {
                        TicketRule ticketRule = new TicketRule();
                        BeanUtils.copyProperties(t, ticketRule);
                        ticketRule.setId(null);
                        ticketRule.setBoard(b);
                        otherBoards.stream().filter(o -> o.getBoard().equals(b)).findAny()
                                .ifPresent(i -> ticketRule.setId(i.getId()));
                        result.add(ticketRule);
                    });
                });
        this.saveOrUpdateBatch(result);
        return ticketRules;
    }

    @Override
    public List<TicketRule> getRules(long tenantId, String board, String name) {
        List<Long> boards = Arrays.stream(StringUtils.commaDelimitedListToStringArray(board))
                .map(s -> Long.parseLong(s.trim())).collect(Collectors.toList());

        List<TicketRule> ticketRules = super.list(Wrappers.<TicketRule>lambdaQuery()
                .eq(TicketRule::getTenantId, tenantId)
                .in(!StringUtils.isEmpty(board), TicketRule::getBoard, boards)
                .eq(!StringUtils.isEmpty(name), TicketRule::getName, name)
                .eq(TicketRule::getEnable, true));

        List<Board> activeBoardList = boardService.list(Wrappers.<Board>lambdaQuery()
                .eq(Board::getTenantId, tenantId)
                .eq(Board::getInactiveFlag, true).select(Board::getCwId));
        if (!CollectionUtils.isEmpty(activeBoardList)) {
            List<Integer> boardIds = activeBoardList.stream().map(Board::getCwId).map(s -> s.intValue()).collect(Collectors.toList());
            return ticketRules.stream().filter(t -> !boardIds.contains(t.getBoard())).collect(Collectors.toList());
        }
        return ticketRules;
    }

    @Override
    public List<TicketRule> getAllBoardRulesByName(TicketRule ticketRule) {
        return baseMapper.getAllBoardRulesByName(ticketRule);
    }

    @Override
    public List<TicketRule> getBoardRules(Long tenantId, Integer board) {
        List<TicketRule> rules = super.list(Wrappers.<TicketRule>lambdaQuery().eq(TicketRule::getTenantId, tenantId)
                .in(TicketRule::getBoard, board).orderByAsc(TicketRule::getName));
        rules.sort(Comparator.comparing(TicketRule::getDisplayName));
        return rules;
    }


    @Override
    public JsonNode getRulesCount(long tenantId) {
        ObjectMapper mapper = new ObjectMapper();
        ObjectNode ruleObj = mapper.createObjectNode();
        Integer count = super.count(Wrappers.<TicketRule>lambdaQuery().eq(TicketRule::getTenantId, tenantId));
        ruleObj.put("result", count);
        return ruleObj;
    }

    @Override
    public Map<String, Object> repairStatus(Long tenantId) {
        Set<TicketRule> resultes = new HashSet<>();
        Set<TicketRule> modifyRes = new HashSet<>();
        Set<Long> delRules = new HashSet<>();
        Set<TicketRule> Rules = new HashSet<>();
        List<TicketRule> rules = super.list(Wrappers.<TicketRule>lambdaQuery()
                .eq(tenantId != null, TicketRule::getTenantId, tenantId)
                .eq(TicketRule::getName, "STATUS_POINTS_GRADE_RULE"));
        for (TicketRule f : rules) {
            Board board = boardService.getOne(Wrappers.<Board>lambdaQuery()
                    .eq(Board::getTenantId, f.getTenantId())
                    .eq(Board::getCwId, f.getBoard()));
            if (board == null) {
                delRules.add(f.getId());
                continue;
            }
            boolean flag = true;
            boolean flag1 = true;
            Iterator<JsonNode> nodeJson = f.getParams().elements();
            while (nodeJson.hasNext()) {
                ObjectNode objectNode = (ObjectNode) nodeJson.next();
                if (!StringUtils.isEmpty(objectNode.get("name"))) {
                    String name = objectNode.get("name").asText().toLowerCase();
                    if (name.contains("process") || name.contains("progress") || name.contains("schedule")) {
                        objectNode.putPOJO("selected", true);
                    }
                    if (flag1) {
                        modifyRes.add(f);
                        flag1 = false;
                    }
                    continue;
                }
                if (flag) {
                    resultes.add(f);
                    flag = false;
                }
                board.setStatuses(board.getExtend().get("status"));
                List<StatusRecord> statusList = this.objectMapper.convertValue(board.getStatuses(), new TypeReference<>() {
                });
                Optional.ofNullable(statusList).ifPresent(s -> {
                    Map<Integer, String> statusMap = s.stream().collect(Collectors.toMap(StatusRecord::getId, StatusRecord::getName));
                    String statusName = statusMap.get(objectNode.get("status").asInt());
                    objectNode.putPOJO("name", statusName == null ? "" : statusName);
                });
                String name = objectNode.get("name").asText().toLowerCase();
                if (name.contains("process") || name.contains("progress") || name.contains("schedule")) {
                    objectNode.putPOJO("selected", true);
                }
                if (flag1) {
                    modifyRes.add(f);
                    flag1 = false;
                }
            }
        }
        super.removeByIds(delRules);
        super.saveOrUpdateBatch(resultes);
        super.saveOrUpdateBatch(modifyRes);
        return Map.of("delRuleIds", delRules,
                "addNameRuleIds", resultes.stream().map(TicketRule::getId).collect(Collectors.toList()),
                "modifySelectedRuleIds", modifyRes.stream().map(TicketRule::getId).collect(Collectors.toList()));
    }

    @Override
    public Boolean initStatus(Long tenantId) {
        String statusDefaultPoints = "[{\"time\": 10, \"point\": 10}, {\"time\": 20, \"point\": 10}, {\"time\": 30, \"point\": 10}, {\"time\": 60, \"point\": 10}, {\"time\": 120, \"point\": 10}, {\"time\": 240, \"point\": 10}, {\"time\": 480, \"point\": 10}, {\"time\": 720, \"point\": 10}, {\"time\": 1440, \"point\": 10}, {\"time\": 2880, \"point\": 10}, {\"time\": 4320, \"point\": 10}, {\"time\": 7200, \"point\": 10}, {\"time\": 11520, \"point\": 10}, {\"time\": 20160, \"point\": 10}, {\"time\": 43200, \"point\": 10}]";
        String contactTitle = "[{\"index\": 0, \"title\": \"CEO\", \"points\": 400, \"selected\": true}, {\"index\": 1, \"title\": \"president\", \"points\": 300, \"selected\": true},{\"index\": 2, \"title\": \"manager\", \"points\": 200, \"selected\": true},{\"index\": 3, \"title\": \"partner\", \"points\": 100, \"selected\": true}]";
        JsonNode pointsNode = null;
        JsonNode contactTitleNode = null;
        try {
            pointsNode = objectMapper.readTree(statusDefaultPoints);
            contactTitleNode = objectMapper.readTree(contactTitle);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        JsonNode finalPointsNode = pointsNode;
        JsonNode finalContactTitleNode = contactTitleNode;
        List<Long> tenantList = new ArrayList<>();
        if (ObjectUtils.isEmpty(tenantId)) {
            tenantList = tenantService.list(Wrappers.<Tenant>lambdaQuery()
                    .eq(Tenant::getStatusId, 1).select(Tenant::getId).orderByAsc(Tenant::getId))
                    .stream().map(Tenant::getId).collect(Collectors.toList());
        } else {
            tenantList.add(tenantId);
        }
        for (Long tenant : tenantList) {
            List<Board> boardList = boardService.list(Wrappers.<Board>lambdaQuery().eq(Board::getTenantId, tenant));
            if (CollectionUtils.isEmpty(boardList)) {
                continue;
            }
            List<TicketRule> ticketRules = new ArrayList<>();
            boardList.forEach(board -> {
                if (!ObjectUtils.isEmpty(board.getExtend().get("status"))) {
                    ArrayNode arrJson = this.objectMapper.createArrayNode();
                    Iterator<JsonNode> statusIterator = board.getExtend().get("status").elements();
                    int index = 1;
                    while (statusIterator.hasNext()) {
                        JsonNode node = statusIterator.next();
                        ObjectNode params = this.objectMapper.createObjectNode();
                        params.putPOJO("index", index++);
                        params.putPOJO("status", node.get("id").asInt());
                        params.putPOJO("name", node.get("name").asText());
                        params.putPOJO("selected", !node.get("closedStatus").asBoolean());
                        params.putPOJO("points", finalPointsNode);
                        arrJson.add(params);
                    }
                    TicketRule ticketRule = TicketRule.builder().board(board.getCwId().intValue()).enable(true)
                            .name("STATUS_POINTS_GRADE_RULE").displayName("Ticket Status").params(arrJson).build();
                    ticketRule.setTenantId(tenant);
                    ticketRules.add(ticketRule);
                }
                //CONTACT_TITLE_POINT_RULE
                TicketRule ticketRule = TicketRule.builder().board(board.getCwId().intValue()).enable(true)
                        .name("CONTACT_TITLE_POINT_RULE").displayName("Contact's Job Title").params(finalContactTitleNode).build();
                ticketRule.setTenantId(tenant);
                ticketRules.add(ticketRule);
            });
            if (!CollectionUtils.isEmpty(ticketRules)) {
                super.remove(Wrappers.<TicketRule>lambdaQuery().eq(TicketRule::getTenantId, tenant)
                        .in(TicketRule::getName, List.of("STATUS_POINTS_GRADE_RULE", "CONTACT_TITLE_POINT_RULE")));
                super.saveBatch(ticketRules);
            }
        }
        return true;
    }

    @Data
    private static class StatusRecord {
        private int id;
        private String name;
    }
}
